#!/usr/bin/env python
#coding=utf-8
import re, os, cPickle, dbhash

class utf8(str):
    ''' This is a subclass of str. it can take unicode or byte str 
    in utf-8 format as parameter, and analyse passed data in compliance
    with utf-8 notation, meaning the characters which were consisted 
    of more than one byte will be picked up, it handle them as integral 
    characters (not byte set anymore). Consequently its method such as 
    len, slice, getitem,etc. will behave just like a native utf-8 string do.
    it can process text data in multibyte utf-8 encodinge conveniently,
    especially useful for east asian languages.
    '''
    
    #this dict is used for from Hex to utf-8 mapping
    
    hexdict={'0':0,'00':0,'01':1,'02':2,'03':3,'04':4,'05':5,'06':6,
        '07':7,'08':8,'09':9,'0a':10,'0b':11,'0c':12,'0d':13,'0e':14,
        '0f':15,'1':1,'10':16,'11':17,'12':18,'13':19,'14':20,'15':21,
        '16':22,'17':23,'18':24,'19':25,'1a':26,'1b':27,'1c':28,'1d':29,
        '1e':30,'1f':31,'2':2,'20':32,'21':33,'22':34,'23':35,'24':36,
        '25':37,'26':38,'27':39,'28':40,'29':41,'2a':42,'2b':43,'2c':44,
        '2d':45,'2e':46,'2f':47,'3':3,'30':48,'31':49,'32':50,'33':51,
        '34':52,'35':53,'36':54,'37':55,'38':56,'39':57,'3a':58,'3b':59,
        '3c':60,'3d':61,'3e':62,'3f':63,'4':4,'40':64,'41':65,'42':66,
        '43':67,'44':68,'45':69,'46':70,'47':71,'48':72,'49':73,'4a':74,
        '4b':75,'4c':76,'4d':77,'4e':78,'4f':79,'5':5,'50':80,'51':81,
        '52':82,'53':83,'54':84,'55':85,'56':86,'57':87,'58':88,'59':89,
        '5a':90,'5b':91,'5c':92,'5d':93,'5e':94,'5f':95,'6':6,'60':96,
        '61':97,'62':98,'63':99,'64':100,'65':101,'66':102,'67':103,'68':104,
        '69':105,'6a':106,'6b':107,'6c':108,'6d':109,'6e':110,'6f':111,'7':7,
        '70':112,'71':113,'72':114,'73':115,'74':116,'75':117,'76':118,'77':119,
        '78':120,'79':121,'7a':122,'7b':123,'7c':124,'7d':125,'7e':126,'7f':127,
        '8':8,'80':128,'81':129,'82':130,'83':131,'84':132,'85':133,'86':134,
        '87':135,'88':136,'89':137,'8a':138,'8b':139,'8c':140,'8d':141,'8e':142,
        '8f':143,'9':9,'90':144,'91':145,'92':146,'93':147,'94':148,'95':149,
        '96':150,'97':151,'98':152,'99':153,'9a':154,'9b':155,'9c':156,'9d':157,
        '9e':158,'9f':159,'a':10,'a0':160,'a1':161,'a2':162,'a3':163,'a4':164,
        'a5':165,'a6':166,'a7':167,'a8':168,'a9':169,'aa':170,'ab':171,'ac':172,
        'ad':173,'ae':174,'af':175,'b':11,'b0':176,'b1':177,'b2':178,'b3':179,
        'b4':180,'b5':181,'b6':182,'b7':183,'b8':184,'b9':185,'ba':186,'bb':187,
        'bc':188,'bd':189,'be':190,'bf':191,'c':12,'c0':192,'c1':193,'c2':194,
        'c3':195,'c4':196,'c5':197,'c6':198,'c7':199,'c8':200,'c9':201,'ca':202,
        'cb':203,'cc':204,'cd':205,'ce':206,'cf':207,'d':13,'d0':208,'d1':209,
        'd2':210,'d3':211,'d4':212,'d5':213,'d6':214,'d7':215,'d8':216,'d9':217,
        'da':218,'db':219,'dc':220,'dd':221,'de':222,'df':223,'e':14,'e0':224,
        'e1':225,'e2':226,'e3':227,'e4':228,'e5':229,'e6':230,'e7':231,'e8':232,
        'e9':233,'ea':234,'eb':235,'ec':236,'ed':237,'ee':238,'ef':239,'f':15,
        'f0':240,'f1':241,'f2':242,'f3':243,'f4':244,'f5':245,'f6':246,'f7':247,
        'f8':248,'f9':249,'fa':250,'fb':251,'fc':252,'fd':253,'fe':254,'ff':255
        }

    bom = chr(0xef) + chr(0xbb) + chr(0xbf)
    __idx_typ_err = TypeError('utf8 indices must be integers')
    __idx_err = IndexError('utf8 index out of range')
    def has_bom(self):
        """     Return True if the utf8 str has a Byte Order Mark before 
        text head
        """
        return True if self.__idx[0]>0 else False
    
    def with_bom(self):
        ''' return a string with a BOM(Byte Order Mark)prepended'''
        return self if self.__idx[0] == 0 else utf8.bom + self
    
    def __new__(cls, data):
        if isinstance(data,unicode):
            data = data.encode('utf-8')
        inst = super(utf8,cls).__new__(cls,data)
        #make a utf-8 to byte string index
        inst.__idx, inst.__len = utf8._analyse(str(inst))
        inst.__start = 0 #iteration start point
        return inst
    #new methods

    def __getitem__(self,i):
        try:
            if i > self.__len :
                raise IndexError
            idx = self.__idx[i]
        except IndexError:
            raise self.__idx_err
        except:
            raise __idx_typ_err
        return str.__getslice__(self,idx,self.__idx[i+1])
    
    def __len__(self):
        return self.__len
    def __iter__(self):
        self.__start = 0
        return self
    def next(self):
        if self.__start < self.__len:
            r = self.__getitem__(self.__start)
            self.__start +=1
            return r
        else:
            raise StopIteration
    def __contains__(self,item):
        if isinstance(sub,unicode): sub = sub.encode('utf-8')
        return str.__contains__(self,item)
    def __getslice__( self, i, j):
        i, j = self.__getrim(i), self.__getrim(j)
        return str.__getslice__(self,i,j)

    def __getrim(self,i):
        try:
            r = self.__idx[i]
        except IndexError:
            r = (self.__idx[-1]-1 if i < 0 else self.__idx[-1])
        except :
            raise
        return r
    #conversion methods
    def decode(self):
        return str.decode(self,'utf-8')
    def unicode(self):
        return str.decode(self,'utf-8')
    #query methods
    def count(self, sub, start = 0, end = None):
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')
        start, end = self.__getrim(start), self.__getrim(end)
        return str.count(self,sub,start,end)
    def endswith(self, sub, start = 0, end = None):
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')
        start, end = self.__getrim(start), self.__getrim(end)
        return str.endswith(self,sub,start,end)
    def startwith(self, sub, start = 0, end = None):
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')
        start, end = self.__getrim(start), self.__getrim(end)
        return str.startswith(self,sub,start,end)

    def find(self, sub, start=0, end=None) :
        'S.find(sub [,start [,end]]) -> int\n\nReturn the lowest index in S where substring sub is found,\nsuch that sub is contained within s[start,end].  Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.'
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')       
        start, end = self.__getrim(start), self.__getrim(end)
        return self.__idx.index(str.find(self,sub,start,end))
    def rfind(self, sub, start=0, end=None) :
        'S.find(sub [,start [,end]]) -> int\n\nReturn the lowest index in S where substring sub is found,\nsuch that sub is contained within s[start,end].  Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.'
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')       
        start, end = self.__getrim(start), self.__getrim(end)
        return self.__idx.index(str.rfind(self,sub,start,end))
    def index(self, sub, start = 0, end = None):
        'S.index(sub [,start [,end]]) -> int\n\nLike S.find() but raise ValueError when the substring is not found.'
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')       
        start, end = self.__getrim(start), self.__getrim(end)
        return self.__idx.index(str.index(self,sub,start,end))
    def rindex(self, sub, start = 0, end = None):
        'S.index(sub [,start [,end]]) -> int\n\nLike S.find() but raise ValueError when the substring is not found.'
        if not end :end = self.__len
        if isinstance(sub,unicode): sub = sub.encode('utf-8')       
        start, end = self.__getrim(start), self.__getrim(end)
        return self.__idx.index(str.rindex(self,sub,start,end))
    #------------------------------
    #padding methods               
    #------------------------------
    def ljust(self, width , fillchar = ' '):
        width = width - self.__len + self.__idx[-1]
        return str.ljust(self,width,fillchar)
    def rjust(self, width , fillchar = ' '):
        width = width - self.__len + self.__idx[-1]
        return str.rjust(self,width,fillchar)
    def zfill(self, width ) :
        width = width - self.__len + self.__idx[-1]
        return str.zfill(self,width)
    def center(self, width, fillchar =' ' ):
        width = width - self.__len + self.__idx[-1]
        return str.center(self, width, fillchar)
    
    #static methods
    @staticmethod
    def fromHex(s, pure = True):
        """     Convert a hexadecimal string to a SINGLE utf-8 character.
        [s] could be string format like '(0x)YYYY', 'u+YYYY',
         '\uYYYY' and  '&#xYYYY;' or a pure integer number
        [pure] means the the string format 1 ('0x' not included) it's for 
        better performance
        """
    ##Extract pure hex str from html entity, unicode escape and U+XXXX etc.
    ##Or if s is a integr, convert it to hex str
        if not pure:
            if s.__class__ is int:
                if s < 128: return chr(s)
                s = hex(s)[2:]
            else:
                s = s.lower()
                match = re.search(r'(?:u\+|\\{0,2}u0{0,3}|&#x)?([a-f0-9]{2,5});?',s)
                if match:
                    s =match.groups()[0]
                else:
                    return None
    ##Deal with corresponding  bytes of the hex
        slen = len(s)
        ##@@@ one byte char, corresponding utf-8 has ONE byte too
        if slen == 2:
            return chr(utf8.hexdict[s])
 
    ##parsing preparation
        #ucs-2 byte sequece acquired by splitting the hex string 's'
        #using if-else rather than iteration to speed up the processing
        hdict = utf8.hexdict
        if slen == 4:
            hseq, blen = [hdict[s[2:4]], hdict[s[0:2]]], 2  #blen is byte length of 's'
        elif slen == 5:
            hseq, blen = [hdict[s[3:5]], hdict[s[1:3]], hdict[s[0]]], 3
        else:   #i.e. slen == 3
            hseq, blen = [hdict[s[1:3]], hdict[s[0]]], 2


    ##bitwise  parsing ------------------------------------------#
        #(0):10aaaaaa ---- '(0)' means first byte of utf8 from right end
        #             ---- aaaaaaaaa is data carried by ucs's first byte
        ##@@@ normal utf-8 byte (0)
        useq = chr(0x80 | hseq[0] & 0x3f) #utf-8 byte sequence 
                    #(0)10-  +  -aaaaaa,   
        
        if blen == 2 and hseq[-1] < 8:
            #Corresponding utf-8 str is 2 bytes long                
            #hence the second byte  of the utf8 has following format
            #(1):110bbb aa 
            useq = chr( 0xc0 | (hseq[1] << 2) | (hseq[0]  >> 6)) + useq
                        #(1)110- + (1)-bbb       +  (1)aa-
        else:
            
            #Corresponding utf8 str is 3 bytes  or  longer
            #(1):10bbbb aa 
            ##@@@ normal utf-8 byte(1)
            useq = chr( 0x80 | (hseq[1] & 0xf) << 2 | hseq[0] >> 6) + useq
                        # (1)10- +   (1)-bbbb          + (1)aa-
            if blen == 2:
                #(2)1110bbbb
                useq = chr( 0xe0 | hseq[1] >> 4) + useq
                            #(2)1110- + (2)bbbb-
            else:
                ##@@@ normal utf-8 byte(2)
                useq = chr( 0x80 | (hseq[2] & 0x3) << 4 | hseq[1] >>4) + useq
                            #(2)10- + (2)-cc               +(2)bbbb-
                useq = chr( 0xf0 | (hseq[2] >> 2)) + useq
                            #(3)11110- + (3)ccc-
        return useq
        
    @staticmethod
    def toHex(s):
        slen = len(s)
        if slen == 1:
            r = hex(ord(s))[2:]
        elif 3 == slen or slen == 2:
            r = repr(s.decode('utf-8'))[4:8]
        else:
            r = repr(s.decode('utf-8'))[7:12]
        return r
    @staticmethod
    def _analyse(s):
        """parse byte str identify every utf-8 character from it, return
        a tuple including a list consisted indices of every character,
        and a integer indicates the sum of all characters
        """
        idx, i, cnt= [0], 0, 0
        #check BOM if found start the idx after it
        s_len = len(s)
        if s_len >= 3 and s[0:3] == utf8.bom:
            i = 3
        idx[0] = i  #this idx has one more item than real utf8 str length,therefore
                    #length of char_n can be calculated by idx[n+1]-idx[n]
        #If the str is empty, return here
        if s_len <= i:
            return idx,0
        #check the primitive str byte for byte, if multibyte char is
        #found, its length will be determined. the Idx for every char will
        #be set according to individual char length
        type_err = lambda byte,i: TypeError("utf8 codec can't decode byte %s in position %d" %(byte,i))
        while i < s_len:
            try:
                byte = ord(s[i])
                if byte >> 7 == 0:
                    step = 1
                elif byte >> 5 == 6 and ord(s[i+1]) >> 6 == 2:
                    step = 2
                elif byte >> 4 == 14 and (ord(s[i+1]) >> 6 == 2
                        and ord(s[i+2]) >> 6 == 2):
                    step = 3
                elif byte >> 3 == 30 and (ord(s[i+1]) >> 6 == 2 and
                        ord(s[i+2]) >> 6 == 2 and
                        ord(s[i+3]) >> 6 == 2):
                    step = 4
                else:
                    raise type_err(byte,i)
                i +=step
                idx.append(i)
                cnt += 1
            except IndexError:
                raise type_err(byte,i)
        return idx,cnt
    
from _bisect import bisect
class cjkv(utf8):
    radicalsA = ["一", "丨", "丶", "丿", "乙", "亅", "二", "亠", "人", "儿", "入", "八", "冂", "冖", "冫", "几", "凵", "刀", "力", "勹", "匕", "匚", "匸", "十", "卜", "卩", "厂", "厶", "又", "口", "囗", "土", "士", "夂", "夊", "夕", "大", "女", "子", "宀", "寸", "小", "尢", "尸", "屮", "山", "巛", "工", "己", "巾", "干", "幺", "广", "廴", "廾", "弋", "弓", "彐", "彡", "彳", "心", "戈", "戶", "手", "支", "攴", "文", "斗", "斤", "方", "无", "日", "曰", "月", "木", "欠", "止", "歹", "殳", "毋", "比", "毛", "氏", "气", "水", "火", "爪", "父", "爻", "爿", "片", "牙", "牛", "犬", "玄", "玉", "瓜", "瓦", "甘", "生", "用", "田", "疋", "疒", "癶", "白", "皮", "皿", "目", "矛", "矢", "石", "示", "禸", "禾", "穴", "立", "竹", "米", "糸", "缶", "网", "羊", "羽", "老", "而", "耒", "耳", "聿", "肉", "臣", "自", "至", "臼", "舌", "舛", "舟", "艮", "色", "艸", "虍", "虫", "血", "行", "衣", "襾", "見", "角", "言", "谷", "豆", "豕", "豸", "貝", "赤", "走", "足", "身", "車", "辛", "辰", "辵", "邑", "酉", "釆", "里", "金", "長", "門", "阜", "隶", "隹", "雨", "靑", "非", "面", "革", "韋", "韭", "音", "頁", "風", "飛", "食", "首", "香", "馬", "骨", "高", "髟", "鬥", "鬯", "鬲", "鬼", "魚", "鳥", "鹵", "鹿", "麥", "麻", "黃", "黍", "黑", "黹", "黽", "鼎", "鼓", "鼠", "鼻", "齊", "齒", "龍", "龜", "龠"]
    radicalsB = ["㐀", "㐃", "丶", "㐅", "㐇", "㐧", "㐩", "㐪", "㐰", "㒫", "㒰", "㒵", "㒺", "㓀", "㓅", "㓘", "㓙", "㓚", "㔓", "㔨", "㔫", "㔯", "㔷", "㔹", "㔽", "㔾", "㕂", "㕕", "㕚", "㕣", "㘝", "㘦", "㚃", "㚅", "㚆", "㚈", "㚎", "㚢", "㜽", "㝉", "㝳", "㝸", "㝼", "㞋", "㞢", "㞦", "㠩", "㠪", "㠯", "㠲", "干", "㡫", "㡯", "㢟", "㢡", "㢤", "㢧", "㣇", "㣉", "㣔", "㣺", "㦮", "㦾", "㧃", "㩺", "㩿", "㪯", "㪲", "㪼", "㫃", "无", "㫐", "㬮", "㬳", "㭁", "㰝", "㱏", "㱙", "㱼", "毋", "㲋", "㲌", "㲳", "㲴", "㲸", "㶡", "㸒", "㸖", "㸚", "㸛", "㸝", "㸦", "㸨", "㹜", "玄", "㺨", "㼉", "㼗", "㽎", "㽒", "用", "㽕", "㽰", "㽱", "癶", "㿝", "㿪", "㿻", "䀎", "䂆", "䂏", "䂖", "䃼", "禸", "䄦", "䆑", "䇂", "䇖", "䉺", "䊵", "䍂", "䍏", "䍧", "䍾", "䎛", "䎟", "䎢", "䎲", "䏋", "䏌", "䑐", "自", "䑒", "䑔", "䑙", "䑝", "䑠", "艮", "䒊", "䒑", "䖈", "䖝", "䘏", "䘕", "䘚", "䙲", "䙷", "䚗", "䚮", "䜪", "䜳", "䝅", "䝖", "䝧", "䞑", "䞖", "䟓", "䠲", "䡂", "䢃", "䢅", "䢊", "䢳", "䣥", "釆", "䤚", "䤛", "䦇", "䦌", "䦹", "隶", "䧱", "䨋", "䨼", "䨽", "䩂", "䩐", "䪏", "䪞", "䪦", "䪱", "䫸", "䬠", "䬢", "䭫", "䭯", "䭴", "䯆", "䯧", "䯭", "䰗", "鬯", "䰙", "䰟", "䰲", "䲥", "䴚", "䴟", "䴬", "䵇", "䵊", "䵑", "䵝", "黹", "䵶", "䵺", "䵽", "䶂", "䶊", "䶒", "䶔", "䶬", "䶯", "䶳"]
    radicalsC = ["𠀀", "𠁡", "𠁼", "𠂆", "𠃉", "𠄌", "𠄞", "𠅁", "𠆢", "𠑶", "𠓛", "𠔀", "𠔼", "𠕳", "𠖬", "𠘧", "𠙴", "𠚣", "𠠲", "𠣌", "𠤎", "𠤬", "𠥭", "𠥻", "𠧒", "𠨍", "𠨬", "𠫓", "𠬚", "𠮙", "𡆠", "𡈼", "𡔛", "𡕒", "𡕞", "𡖄", "𡗒", "𡚦", "𡤼", "𡦹", "𡬝", "𡭔", "𡯁", "𡰣", "𡳾", "𡴭", "𡿦", "𢀑", "𢀳", "𢁒", "𢆉", "𢆯", "𢇗", "𢌗", "𢌬", "𢍺", "𢎗", "𢑏", "𢒀", "𢒼", "𢖩", "𢦌", "𢨤", "𢩥", "𢺵", "𢻫", "𣁁", "𣁬", "𣂑", "𣃗", "𣄭", "𣄻", "𣌡", "𣍝", "𣎳", "𣢀", "𣥂", "𣦵", "𣪂", "𣫬", "𣬂", "𣬛", "𣱅", "𣱕", "𣱱", "𤆂", "𤓯", "𤕎", "𤕜", "𤕪", "𤖨", "𤘅", "𤘔", "𤜚", "𤣥", "𤣩", "𤫪", "𤬦", "𤮺", "𤯓", "𤰃", "𤰒", "𤴓", "𤴥", "𤼥", "𤼽", "𤿆", "𥀿", "𥃤", "𥍝", "𥎦", "𥐕", "𥘅", "𥜻", "𥝌", "𥤢", "𥩕", "𥫗", "𥸥", "𥾅", "𦈢", "𦉪", "𦍋", "𦏲", "𦒱", "𦓎", "𦓤", "𦔮", "𦘒", "𦘩", "𦣝", "𦣹", "𦤳", "𦥑", "𦧆", "𦧾", "𦨇", "𦫋", "𦫓", "𦫳", "𧆛", "𧈝", "𧖧", "𧗝", "𧘇", "𧟠", "𧠆", "𧢲", "𧥛", "𧮫", "𧯚", "𧰦", "𧲠", "𧴤", "𧹘", "𧺆", "𧾷", "𨈏", "𨊠", "𨐋", "𨑃", "𨑍", "𨙨", "𨟰", "𨤏", "𨤢", "𨤽", "𨱗", "𨳇", "𨸏", "𨽶", "𨾅", "𩁶", "𩇕", "𩇦", "𩈃", "𩉛", "𩎒", "𩐁", "𩐗", "𩑋", "𩖘", "𩙱", "𩙿", "𩠐", "𩠺", "𩡧", "𩨑", "𩪿", "𩫴", "𩰊", "𩰠", "𩰫", "𩱹", "𩵋", "𩾏", "𪉖", "𪊋", "𪋼", "𪎑", "𪎳", "𪏭", "𪐗", "𪓋", "𪓑", "𪔂", "𪔋", "𪔸", "𪖐", "𪗄", "𪗔", "𪚑", "𪚦", "𪛊"]
    def __lt__(self,other):
        if other.__class__ is unicode or isinstance(other,str):
            return True if self.__cmp(other) < 0 else False
        else:
            return utf8.__cmp__(self,other)
    def __gt__(self,other):
        if other.__class__ is unicode or isinstance(other,str):
            return True if self.__cmp(other) > 0 else False
        else:
            return utf8.__cmp__(self,other)
    def __eq__(self,other):
        if other.__class__ is unicode or isinstance(other,str):
            return True if self.__cmp(other) == 0 else False
        else:
            return utf8.__cmp__(self,other)
    def __ne__(self,other):
        if other.__class__ is unicode or isinstance(other,str):
            return True if self.__cmp(other) != 0 else False
        else:
            return utf8.__cmp__(self,other)

    def __le__(self,other):
        if other.__class__ is unicode or isinstance(other,str):
            return True if self.__cmp(other) <= 0 else False
        else:
            return utf8.__cmp__(self,other)
    def __le__(self,other):
        if other.__class__ is unicode or isinstance(other,str):
            return True if self.__cmp(other) >= 0 else False
        else:
            return utf8.__cmp__(self,other)
    
        
    def __cmp(self, other):
        ##TODO: this function is too sluggish, it should be re write
        ##in pyrex or some other lo-level language
        if other.__class__ is unicode or other.__class__ is str:
            other = utf8(other)

        #determin iteration length
        #if these two item are equal after char by char comparison  
        #their length will decide the result
        otherlen = len(other)
        selflen = len(self)
        if selflen < otherlen :
            length = selflen
            result = -1
        elif selflen >otherlen:
            length = otherlen
            result = 1  
        else:
            length = otherlen
            result =0
        #-------------char by char comparison begin------------------#    
        for i  in xrange(length):
            cs, co  =  self[i], other[i]
            cslen,colen = len(cs), len(co)
            if (cslen < 3 or colen < 3) and cs != co:
                return -1 if cs < co else 1
 
            else:
                csradical,coradical = cjkv.radicalinfo(cs),cjkv.radicalinfo(co)
                
                if csradical  and coradical: # both are cjkv characters
                    #compare radical
                    if csradical['sn'] < coradical['sn']:
                        return -1
                    elif csradical['sn'] > coradical['sn']:
                        return 1
                    else:
                        #compare area
                        if csradical['area'] < coradical['area']:
                            return -1
                        elif csradical['area'] > coradical['area']:
                            return 1
                        
                        # same radical same area
                        elif cs!= co:
                                return -1 if cs < co else 1 
                            
                #one or both of them are not cjkv
                elif cs!= co:
                        return -1 if cs < co else 1

        return result
                
                
    @staticmethod    
    def hanArea (char):
        ''' take one utf-8 character as parameter, return a integer 
        which indicats which hanzi(kanji,hanja) area 
        it belongs to (according to its radical):
            
        1:basic cjkv unified area, 
        2:cjkv unified extension area A,
        3:cjkv unified extension area C,
        or None for the char is not a hanzi
        '''
        Afirst, Alast = '一', '龥'
        Bfirst, Blast = '㐀', '䶵'
        Cfirst, Clast = '𠀀', '𪛖'

        if Afirst <= char <= Alast:
            return 1
        elif Bfirst <= char <= Blast:
            return 2
        elif Cfirst <= char <= Clast:
            return 3
        else:
            return None

    @staticmethod        
    def radicalinfo(char):
        hanarea = cjkv.hanArea(char)
        if not hanarea: return None
        if hanarea == 1:
            i = bisect(cjkv.radicalsA, char)-1
        elif hanarea == 2:
            i = bisect(cjkv.radicalsB, char)-1
        else:
            i = bisect(cjkv.radicalsC, char)-1
        return {'radical':cjkv.radicalsA[i],'sn':i,'area':hanarea}
